package graph;

import graph.IdManager.IdText;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.Set;

import util.Common;

import mapreduce.BytesFormat;
import mapreduce.IntegerFormat;
import mapreduce.Record;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.EnvironmentLockedException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.StatsConfig;

public class PropertyManager {

	public static class PropertyRecord{
		public PropertyRecord(int id, Record.RecordData data){
			m_id = id;
			m_data = data;
		}
		
		public int m_id;
		public Record.RecordData m_data;
	}
	
	public static class PropertyManagerIterator {
		private PropertyRecord m_nextRecord;
		public PropertyManagerIterator(PropertyManager manager) {
			m_db = manager.m_db;
			try {
				Cursor cursor = m_db.openCursor(null, null);
				m_cursor = cursor;
				m_nextRecord = getNext();
			} catch (DatabaseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public boolean hasNext() {
			if (m_nextRecord == null) {
				return false;
			} else {
				return true;
			}
		}

		public PropertyRecord next() {
			PropertyRecord record = m_nextRecord;
			m_nextRecord = getNext();
			return record;
		}

		private PropertyRecord getNext() {
			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry data = new DatabaseEntry();
			OperationStatus status;
			try {
				status = m_cursor.getNext(key, data, LockMode.DEFAULT);
			} catch (DatabaseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				status = OperationStatus.NOTFOUND;
			}
			if (status == OperationStatus.NOTFOUND) {
				return null;
			} else {
				
				PropertyRecord record = new PropertyRecord(getKeyValue(key), getDataValue(data));
				return record;
			}
		}

		Database m_db;
		Cursor m_cursor;
		int m_id;
		Record.RecordData m_data;
	}

	public static PropertyManager load(File file) {
		return new PropertyManager(file);
	}

	public void setProperty(int id, Record.RecordData data) {
		DatabaseEntry key = getKeyEntry(id);
		DatabaseEntry value = getValueEntry(data);
		try {
			m_db.put(null, key, value);
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public Object getProperty(int id) {
		DatabaseEntry key = getKeyEntry(id);
		DatabaseEntry value = new DatabaseEntry();
		try {
			m_db.get(null, key, value, LockMode.DEFAULT);
			byte[] bytes = value.getData();
			Record.RecordData data = BytesFormat.decodeBuffer(bytes);
			return data.getData();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public void sync() {
		try {
			m_env.sync();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	static private DatabaseEntry getKeyEntry(int id) {
		ByteBuffer buffer = ByteBuffer.allocate(4);
		buffer.putInt(id);
		DatabaseEntry entry = new DatabaseEntry(buffer.array());
		return entry;
	}
	
	static private int getKeyValue(DatabaseEntry entry){
		ByteBuffer buffer = ByteBuffer.wrap(entry.getData());
		return buffer.getInt();
	}

	static private DatabaseEntry getValueEntry(Record.RecordData data) {
		byte[] bytes = BytesFormat.encodeBuffer(data);
		DatabaseEntry entry = new DatabaseEntry(bytes);
		return entry;
	}
	
	static private Record.RecordData getDataValue(DatabaseEntry entry){
		return BytesFormat.decodeBuffer(entry.getData());
	}

	private Environment m_env;
	private Database m_db;

	private PropertyManager(File file) {
		String dirPath = file.getParent();
		String name = file.getName();

		try {
			EnvironmentConfig envConfig = new EnvironmentConfig();
			envConfig.setAllowCreate(true);
			m_env = new Environment(new File(dirPath), envConfig);
			DatabaseConfig dbConfig = new DatabaseConfig();
			dbConfig.setAllowCreate(true);
			m_db = m_env.openDatabase(null, name, dbConfig);
		} catch (EnvironmentLockedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	static public void main(String[] args) {
		int argIndex = 0;
		String option = args[argIndex++];
		if (option.equals("--favorite")) {
			// get favorite property manager from favorite edges db
			String edgesDirPath = args[argIndex++];
			File favoritePropertyFile = new File(args[argIndex++]);
			PropertyManager propertyManager = new PropertyManager(
					favoritePropertyFile);
			EdgesImplFactory implFactory = new DbEdgesImplFactory(edgesDirPath);
			String edgesName = "favorite";
			DirectedEdges favoriteEdges = new DirectedEdges(implFactory,
					edgesName);
			Set<Integer> tweetIds = favoriteEdges.getDesNodes();
			for (int tweetId : tweetIds) {
				int favoriteNum = favoriteEdges.getEdgesTo(tweetId).size();
				propertyManager.setProperty(tweetId, new Record.RecordData(
						favoriteNum, new IntegerFormat()));
			}
			propertyManager.sync();
			System.out.println(propertyManager.getProperty(5));
			PropertyManager manager = Common.getPropertyManager("favorite");
			System.out.println(manager.getProperty(5));
		}
		if (option.equals("--test")) {
			PropertyManager manager = Common.getPropertyManager("favorite");
			PropertyManagerIterator it = new PropertyManagerIterator(manager);
			while(it.hasNext()){
				PropertyRecord record = it.next();
				System.out.printf("%d %d\n", record.m_id, record.m_data.getData());
			}
		}
	}
}
